package com.zmj.spring;

import com.zmj.annotaion.Autowired;
import com.zmj.annotaion.Component;
import com.zmj.annotaion.ComponentScan;
import com.zmj.annotaion.Scope;
import com.zmj.aware.BeanNameAware;
import com.zmj.interfaces.InitializingBean;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class AnnotationConfigApplicationContext {

    private Class configClazz;

    private HashMap<String, Object> singletonBeanMap = new HashMap<>(); // 一级缓存（存在 成品bean（实例化）

    private HashMap<String, Object> earlySingletonBeanMap = new HashMap<>(); // 二级缓存  半成品bean（未初始化完）


    private HashMap<String, BeanDefinition> beanDefinitionMap = new HashMap<>(); // 所有bean的定义信息


    private AnnotationConfigApplicationContext() {
    }

    public AnnotationConfigApplicationContext(Class configClazz) {

        this.configClazz = configClazz;

        //扫描解析配置类
        scanner(configClazz);

        //注册bean
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {

            String name = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();
            String scopeValue = beanDefinition.getScope();


            beanDefinition.getClazz().getDeclaredFields();

            // 单例bean则创建存放单例池中
            if (scopeValue.equalsIgnoreCase("singleton")) {
                if (singletonBeanMap.get(name) == null) {
                    Object bean = createBean(name, beanDefinition);
                    //放入一级缓存
                    singletonBeanMap.put(name, bean);
                    //清空二级缓存
                    earlySingletonBeanMap.remove(name);
                }
            }


        }
    }

    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getClazz();
        Object object;
        try {

            // 二级缓存中存在? 返回/创建
            object = earlySingletonBeanMap.containsKey(beanName) ? earlySingletonBeanMap.get(beanName) : clazz.getDeclaredConstructor().newInstance();

            //将创建半成品bean提前暴露
            earlySingletonBeanMap.put(beanName, object);


            // 属性注入
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    String autoBeanName = field.getName();

                    // 查询属性bean的信息
                    BeanDefinition autoBeanDefinition = beanDefinitionMap.get(autoBeanName);
                    if (autoBeanDefinition == null) {
                        throw new RuntimeException("Autowired error , no such bean of " + autoBeanName);
                    }

                    // 一级/二级缓存是否存在？
                    Object singleObj = singletonBeanMap.containsKey(autoBeanName) ? singletonBeanMap.get(autoBeanName) : earlySingletonBeanMap.get(autoBeanName);

                    if (singleObj == null) { //一级+二级不存在
                        try {
                            //创建对象
                            Class clazz1 = autoBeanDefinition.getClazz();
                            singleObj = clazz1.newInstance();
                            //设置属性
                            field.setAccessible(true);
                            field.set(object, singleObj);
                            //放入二级
                            earlySingletonBeanMap.put(autoBeanName, singleObj);
                        } catch (IllegalAccessException | InstantiationException e) {
                            e.printStackTrace();
                        }
                    } else {
                        // 缓存中存在，设置属性
                        field.setAccessible(true);
                        field.set(object, singleObj);
                    }
                }
            }

            //Aware回调
            if (object instanceof BeanNameAware) {
                ((BeanNameAware) object).setBeanName(beanName);
            }

            //初始化
            if (object instanceof InitializingBean) {
                ((InitializingBean) object).afterPropertiesSet();
            }

            return object;
        } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解析配置类 -> beanDefinition -> beanDefinitionMap
     *
     * @param configClass
     */
    private void scanner(Class configClass) {

        //配置类也需要作为bean注入
        createConfigBean(configClass);

        // 无ComponentScan，结束IOC流程
        if (!configClass.isAnnotationPresent(ComponentScan.class)) {
            return;
        }

        //获取ComponentScan扫描路径
        ComponentScan componentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        String componentScanPagePath = componentScan.value();


        //获取扫描路径下文件
        ClassLoader classLoader = AnnotationConfigApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(componentScanPagePath.replace(".", "/"));

        File file = new File(resource.getFile());
        if (file.isDirectory())
            for (File f : file.listFiles()) {

                // 非class类不处理
                if (!f.getName().endsWith(".class")) {
                    continue;
                }

                Class<?> loadClass;
                try {
                    //处理路径并加载类
                    String classes = f.getPath().substring(f.getPath().indexOf("classes") + 8, f.getPath().indexOf(".class")).replaceAll("/", ".").replace("\\\\", ".");
                    loadClass = classLoader.loadClass(classes);


                    //存在Component注解则为需加载的bean
                    if (loadClass.isAnnotationPresent(Component.class)) {

                        //获取bean名称，默认为类名首字母小写
                        String beanName = loadClass.getDeclaredAnnotation(Component.class).value();
                        if (StringUtils.isEmpty(beanName)) {
                            beanName = loadClass.getSimpleName().substring(0, 1).toLowerCase() + loadClass.getSimpleName().substring(1);
                        }

                        //获取作用域(默认单例)
                        String scopeValue = loadClass.isAnnotationPresent(Scope.class) ? loadClass.getDeclaredAnnotation(Scope.class).value() : "singleton";

                        //存储bean信息
                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setScope(scopeValue);
                        beanDefinition.setClazz(loadClass);
                        beanDefinitionMap.put(beanName, beanDefinition);
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
    }

    /**
     * 单个class作为单例bean注入的构成
     *
     * @param configClass
     */
    private void createConfigBean(Class configClass) {
        Object obj = null;
        try {
            obj = configClass.newInstance();
        } catch (IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
        String beanName = configClass.getSimpleName().substring(0, 1).toLowerCase() + configClass.getSimpleName().substring(1);

        BeanDefinition beanDefinition = new BeanDefinition();
        beanDefinition.setScope("singleton");
        beanDefinition.setClazz(configClass);
        beanDefinitionMap.put(beanName, beanDefinition);
        singletonBeanMap.put(beanName, obj);
        return;
    }

    /**
     * 获取bean
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        //判断bean是否存在
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new RuntimeException("No Such Bean Exception");
        }

        if (beanDefinition.getScope().equalsIgnoreCase("prototype")) {
            return createBean(beanName, beanDefinition);//非单例bean则创建
        }

        return singletonBeanMap.get(beanName);//单例池返回

    }

    /**
     * 获取容器已加载的所有对象
     *
     * @return
     */
    public Set<String> getBeanNames() {
        return beanDefinitionMap.keySet();
    }
}
